package com.android.love.net;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.view.Window;

import com.android.love.MyApplication;
import com.android.love.ui.BetterActivity;
import com.android.love.util.LogUtil;


/**
 * 和AsyncTask工作原理相似，但提供一些额外的功能
 * 
 * 1）它保持跟踪每一个Context的活动实例，确保可以传递正确的实例。这当activity被强迫进入后台状态或者用户旋转屏幕是非常有用的
 * 
 * 
 * 2）progress dialog会自动show，参考{@link #useCustomDialog(int)}和
 * {@link #disableDialog()} （方法废弃 dialogId=-1）
 * 
 * 
 * 3）如果在 {@link #doInBackground(Object...)}抛出异常，会在
 * {@link #handleError(Context, Exception)}做相应处理
 * 
 * 
 * 4）你不应该再重写onPreExecute(), doInBackground() 和 onPostExecute()，相应的，应该分别重写
 * {@link #before(Context)},{@link #doCheckedInBackground(Context, Object...)}和
 * {@link #after(Context, Object)}
 * 
 * 
 * 这些特点需要继承application {@link MyApplication}
 * 
 * @param <ParameterT>
 * @param <ProgressT>
 * @param <ReturnT>
 */
public abstract class BetterAsyncTask<ParameterT, ProgressT, ReturnT> extends
		AsyncTask<ParameterT, ProgressT, ReturnT> {
	private static final String TAG = "BetterAsyncTask";
	private final MyApplication appContext;
	private final boolean contextIsDroidFuActivity;

	private Exception error;

	private boolean isTitleProgressEnabled,
			isTitleProgressIndeterminateEnabled = true;

	private final String callerId;

	private BetterAsyncTaskCallable<ParameterT, ProgressT, ReturnT> callable;

	private int dialogId = -1;

	/**
	 * Creates a new BetterAsyncTask who displays a progress dialog on the
	 * specified Context.
	 * 
	 * @param context
	 */
	public BetterAsyncTask(Context context) {

		if (!(context.getApplicationContext() instanceof MyApplication)) {
			throw new IllegalArgumentException(
					"context bound to this task must be a DroidFu context (DroidFuApplication)");
		}
		this.appContext = (MyApplication) context.getApplicationContext();
		this.callerId = context.getClass().getCanonicalName();
		this.contextIsDroidFuActivity = context instanceof BetterActivity;

		appContext.setActiveContext(callerId, context);

		if (contextIsDroidFuActivity) {
			int windowFeatures = ((BetterActivity) context).getWindowFeatures();
			if (Window.FEATURE_PROGRESS == (Window.FEATURE_PROGRESS & windowFeatures)) {
				this.isTitleProgressEnabled = true;
			} else if (Window.FEATURE_INDETERMINATE_PROGRESS == (Window.FEATURE_INDETERMINATE_PROGRESS & windowFeatures)) {
				this.isTitleProgressIndeterminateEnabled = true;
			}
		}
	}

	/**
	 * Gets the most recent instance of this Context. This may not be the
	 * Context used to construct this BetterAsyncTask as that Context might have
	 * been destroyed when a incoming call was received, or the user rotated the
	 * screen.
	 * 
	 * @return The current Context, or null if the current Context has ended,
	 *         and a new one has not spawned.
	 */
	protected Context getCallingContext() {
		try {
			Context caller = (Context) appContext.getActiveContext(callerId);
			if (caller == null
					|| !this.callerId.equals(caller.getClass()
							.getCanonicalName())
					|| (caller instanceof Activity && ((Activity) caller)
							.isFinishing())) {
				// the context that started this task has died and/or was
				// replaced with a different one
				return null;
			}
			return caller;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	protected final void onPreExecute() {
		Context context = getCallingContext();
		if (context == null) {
			Log.d(BetterAsyncTask.class.getSimpleName(),
					"skipping pre-exec handler for task " + hashCode()
							+ " (context is null)");
			cancel(true);
			return;
		}

		LogUtil.i(TAG, "onPreExecute" + contextIsDroidFuActivity + "");
		if (contextIsDroidFuActivity) {
			Activity activity = (Activity) context;
			if (dialogId > -1) {
				LogUtil.i(TAG, "onPreExecute" + "showDialog");
				activity.showDialog(dialogId);
			}
			if (isTitleProgressEnabled) {
				activity.setProgressBarVisibility(true);
			} else if (isTitleProgressIndeterminateEnabled) {
				activity.setProgressBarIndeterminateVisibility(true);
			}
		}
		before(context);
	}

	/**
	 * Override to run code in the UI thread before this Task is run.
	 * 
	 * @param context
	 */
	protected void before(Context context) {
	}

	@Override
	protected final ReturnT doInBackground(ParameterT... params) {
		ReturnT result = null;
		Context context = getCallingContext();
		try {
			result = doCheckedInBackground(context, params);
		} catch (Exception e) {
			this.error = e;
		}
		return result;
	}

	/**
	 * Override to perform computation in a background thread
	 * 
	 * @param context
	 * @param params
	 * @return
	 * @throws Exception
	 */
	protected ReturnT doCheckedInBackground(Context context,
			ParameterT... params) throws Exception {
//		if (callable != null) {
//			return callable.call(this);
//		}
		return null;
	}

	/**
	 * Runs in the UI thread if there was an exception throw from
	 * doCheckedInBackground
	 * 
	 * @param context
	 *            The most recent instance of the Context that executed this
	 *            BetterAsyncTask
	 * @param error
	 *            The thrown exception.
	 */
	protected abstract void handleError(Context context, Exception error);

	@Override
	protected final void onPostExecute(ReturnT result) {
		Context context = getCallingContext();
		if (context == null) {
			Log.d(BetterAsyncTask.class.getSimpleName(),
					"skipping post-exec handler for task " + hashCode()
							+ " (context is null)");
			return;
		}
		LogUtil.i(TAG, "onPostExecute" + contextIsDroidFuActivity + "");
		if (contextIsDroidFuActivity) {
			Activity activity = (Activity) context;
			if (dialogId > -1) {
				LogUtil.i(TAG, "removeDialog");
				activity.removeDialog(dialogId);
			}
			if (isTitleProgressEnabled) {
				activity.setProgressBarVisibility(false);
			} else if (isTitleProgressIndeterminateEnabled) {
				activity.setProgressBarIndeterminateVisibility(false);
			}
		}

		if (failed()) {
			handleError(context, error);
		} else {
			after(context, result);
		}
	}

	/**
	 * A replacement for onPostExecute. Runs in the UI thread after
	 * doCheckedInBackground returns.
	 * 
	 * @param context
	 *            The most recent instance of the Context that executed this
	 *            BetterAsyncTask
	 * @param result
	 *            The result returned from doCheckedInBackground
	 */
	protected abstract void after(Context context, ReturnT result);

	/**
	 * Has an exception been thrown inside doCheckedInBackground()
	 * 
	 * @return
	 */
	public boolean failed() {
		return error != null;
	}

	/**
	 * Use a BetterAsyncTaskCallable instead of overriding
	 * doCheckedInBackground()
	 * 
	 * @param callable
	 */
	public void setCallable(
			BetterAsyncTaskCallable<ParameterT, ProgressT, ReturnT> callable) {
		this.callable = callable;
	}

	/**
	 * Use a custom resource ID for the progress dialog
	 * 
	 * @deprecated
	 * @param dialogId
	 */
	public void useCustomDialog(int dialogId) {
		this.dialogId = dialogId;
	}

	/**
	 * Disable the display of a dialog during the execution of this task.
	 * @deprecated
	 */
	public void disableDialog() {
		this.dialogId = -1;
	}
}
